#ifndef SOCKET_SERVER_CONNECTION_H_
#define SOCKET_SERVER_CONNECTION_H_

#include "socket_address.h"

#include <arpa/inet.h>
#include <cstdint>
#include <functional>
#include <memory>

class SocketServerConnection;
class SocketServer
{
public:
    enum EmConnectionState
    {
        EmConnectionInit = 0,
        EmConnectionConnected,
        EmConnectionDisConnected,
        EmConnectionHandshaking,
    };
    struct Message
    {
        uint8_t *     m_data{};
        int           m_data_length{};
        int           m_use_length{};
        SocketAddress m_peer_address{}; // for udp
    };
    using OnNewConn = std::function<void(const std::shared_ptr<SocketServerConnection> & connection)>;
    using OnMessage = std::function<void(const std::shared_ptr<SocketServerConnection> & connection, int result, Message & message)>;
    using OnDisConn = std::function<void(const std::shared_ptr<SocketServerConnection> & connection, int result)>;
    using OnError   = std::function<void(const std::shared_ptr<SocketServerConnection> & connection, int result)>;
    struct Option
    {
        std::string m_bind_addr;
        uint16_t    m_listen_port;
        int         m_use_ipv6;
        std::string m_crt_file;
        std::string m_private_key_file;
        std::string m_ca_file;
        std::string m_domain_name;
        int         m_enable_nonblock;
    };
public:
    SocketServer()= default;
    virtual ~SocketServer()                        = default;
    virtual int BindAddress(const Option & option) = 0;
    virtual int Listen()                           = 0;
    void        SetOnConnection(OnNewConn && conn)
    {
        m_on_conn_cb = conn;
    }
    void SetOnMessage(OnMessage && message)
    {
        m_on_message_cb = message;
    }
    void SetOnDisConn(OnDisConn && disconn)
    {
        m_on_dis_conn_cb = disconn;
    }
    void SetOnError(OnError && errconn)
    {
        m_on_error_cb = errconn;
    }

protected:
    Option    m_option;
    OnNewConn m_on_conn_cb;
    OnMessage m_on_message_cb;
    OnDisConn m_on_dis_conn_cb;
    OnError   m_on_error_cb;
};

class SocketServerConnection
{
public:
    struct ClientOption
    {
        int m_enable_nonblock;
        std::string m_crt_file;
        std::string m_private_key_file;
        std::string m_ca_file;
    };
    virtual ~SocketServerConnection() = default;

    virtual int AsyncWrite(const uint8_t * data, int data_length) = 0;
    virtual int AsyncWrite(const uint8_t * data, const int data_length, const SocketAddress & addr)
    {
        return 0;
    }
    virtual int  AsyncRead() = 0;
    virtual void Close()     = 0;
    virtual void UpdateOption(const ClientOption & option) = 0;
    virtual int  ConnectionState()
    {
        return m_connect_state;
    }
    virtual void UpdateConnState(int state)
    {
        m_connect_state = state;
    }
    SocketAddress PeerAddress() const
    {
        return m_address;
    }
    void UpdatePeerAddress(const SocketAddress& address)
    {
        m_address = address;
    }
    void SetOnConnect(SocketServer::OnNewConn && conn)
    {
        m_on_connect_cb = conn;
    }
    void SetOnMessage(SocketServer::OnMessage && msg)
    {
        m_on_message_cb = msg;
    }
    void SetOnDisConn(SocketServer::OnDisConn && dis)
    {
        m_on_dis_conn_cb = dis;
    }
    void setOnError(SocketServer::OnError     && err)
    {
        m_on_error_cb = err;
    }
protected:
    SocketServer::OnNewConn m_on_connect_cb;// for tls
    SocketServer::OnMessage m_on_message_cb;
    SocketServer::OnDisConn m_on_dis_conn_cb;
    SocketServer::OnError   m_on_error_cb;
private:
    int           m_connect_state = 0;
    SocketAddress m_address;
};

#endif
